﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Widgetsphere.Core.Util;
using System.CodeDom.Compiler;
using System.IO;

namespace Widgetsphere.Core.Util
{
	public static class LINQDynamicCompile
	{
		private static Dictionary<string, MethodInfo> queryDictionary = new Dictionary<string, MethodInfo>();

		public static MethodInfo GetMethod(string linq, string businessNamespace, string objectName)
		{
			string hash = HashHelper.ComputeHash(linq);

			MethodInfo method = null;
			if (!queryDictionary.ContainsKey(hash))
			{
				method = LINQDynamicCompile.CreateScriptClass(Guid.NewGuid(), businessNamespace, linq, objectName);
				queryDictionary.Add(hash, method);
			}
			else
				method = queryDictionary[hash];
			return method;

		}

		private static MethodInfo CreateScriptClass(System.Guid currentGuid, string businessNamespace, string linq, string ObjectName)
		{
			string guid = currentGuid.ToString("N");
			StringBuilder sb = new StringBuilder();
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Linq;");
			sb.AppendLine("using System.Linq.Expressions;");
			sb.AppendLine("using " + businessNamespace + ".Objects;");
			sb.AppendLine("using " + businessNamespace + ".LINQ;");
			sb.AppendLine("namespace Widgetpshere.MemoryGeneration");
			sb.AppendLine("{");
			sb.AppendLine("public static class " + ObjectName + "Queries" + guid);
			sb.AppendLine("{");
			sb.AppendLine("    public static " + ObjectName + "Collection  ZZ" + guid + "(" + ObjectName + "Paging paging)");
			sb.AppendLine("    {");
			sb.AppendLine("        return " + ObjectName + "Collection.RunSelect(" + linq + ", paging);");
			sb.AppendLine("    }");
			sb.AppendLine("}");
			sb.AppendLine("}");


			try
			{
				CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
				CompilerParameters cp = new CompilerParameters();
				//TODO: Set Uri Through Different Variable
				Uri uri = new Uri(Assembly.GetExecutingAssembly().CodeBase);
				FileInfo fi = new FileInfo(uri.AbsolutePath);
				string appServerBinDirectory = fi.DirectoryName;
				if (!string.IsNullOrEmpty(appServerBinDirectory))
				{
					cp.CompilerOptions = string.Format(@"/lib:""{0}""", appServerBinDirectory);
				}
				cp.ReferencedAssemblies.Add("System.dll");
				cp.ReferencedAssemblies.Add("System.Core.dll");
				cp.ReferencedAssemblies.Add(businessNamespace.Remove(businessNamespace.Length - 9) + ".dll");
				cp.ReferencedAssemblies.Add("Widgetsphere.Core.dll");
				cp.GenerateExecutable = false;
				cp.GenerateInMemory = true;
				CompilerResults cr = codeDomProvider.CompileAssemblyFromSource(cp, sb.ToString());
				if (cr.Errors.HasErrors)
				{
					StringBuilder error = new StringBuilder();
					error.Append("Error Compiling Expression: ");
					foreach (CompilerError err in cr.Errors)
					{
						error.AppendFormat("{0}\n", err.ErrorText);
					}
					throw new Exception("Error Compiling Expression: " + error.ToString());
				}
				Assembly assembly = cr.CompiledAssembly;
				Type[] types = assembly.GetTypes();
				if (types.Length == 1)
					return types[0].GetMethod("ZZ" + guid);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return null;
		}

	}

}